<?php

namespace common\service\project_warning_rules;

use support\ErrorCode;
use common\model\TaskModel;
use support\SingletonTrait;
use common\model\EntityModel;
use common\service\StatusService;
use common\service\OptionsService;
use common\model\StepCategoryModel;
use common\exception\LogicException;

/**
 * 计划封板剧集临近 环节完成检查
 */
class EpisodeSealDateTaskProgressCheck implements ProjectWarningRuleInterface
{
    use SingletonTrait;

    const CHECK_CODE = 'EPISODE_SEAL_DATE_TASK_PROGRESS_CHECK';

    const NEED_PARAM = ['project_id'];

    private $errorMessage = [];

    /**
     * 检查参数是否包含
     * @param $param
     * @return void
     */
    public function paramRequired($param)
    {
        foreach (self::NEED_PARAM as $paramKey) {
            if (!isset($param[$paramKey])) {
                throw new LogicException("param {$paramKey} needed", ErrorCode::DATA_INVALID);
            }
        }
    }

    /**
     * 检查是否满足
     * @param $checkData
     * @return bool
     * @throws \think\Exception
     */
    public function check($checkData)
    {
        $this->paramRequired($checkData);
        $projectId = $checkData['project_id'];
        /**
         * 检查设置了封板日期,但是没有播出的剧集 检查剧集进度
         */
        $queryFields = [
            'id',
            'episode_is_seal',
            'episode_is_show',
            'episode_plan_seal_time',
            'code',
        ];
        $filter = [
            "episode.episode_plan_seal_time" => ['GT', 0],
        ];
        $episodeList = model(EntityModel::class)
            ->alias('episode')
            ->field(transform_custom_fields($queryFields, 'episode'))
            ->where(['episode.project_id' => $projectId, 'episode.module_id' => module_by_code('episode')['id']])
            ->where(['episode.code' => ['LIKE', 'EP%']])
            ->where(parse_filter_to_tp_where($filter, module_by_code('episode')))
            ->where("(episode.json->>'$.episode_is_delete' is null or episode.json->>'$.episode_is_delete' = 'no') and (episode.json->>'$.episode_is_seal' is null or episode.json->>'$.episode_is_seal' = 'no') and (episode.json->>'$.episode_is_show' is null or episode.json->>'$.episode_is_show' = 'no')")
            ->select();

        if (empty($episodeList)) {
            return true;
        }
        $limitHour = $this->getEpisodeLimitHourCheckConfig();
        if (empty($limitHour)) {
            return true;
        }
        $limitSecond = $limitHour * 3600;
        $now = time();
        $errorEpisodeCodeList = [];
        $errorEpisodeTimeList = [];
        foreach ($episodeList as $item) {
            $errorEpisodeTimeList[$item['code']] = $surplusTime = $item['episode_plan_seal_time'] - $now;
            if ($surplusTime <= 0) {
                $errorEpisodeCodeList[] = $item['code'];
            } else if ($surplusTime < $limitSecond) {
                // 达到检查的阈值
                $errorEpisodeCodeList[] = $item['code'];
            }
        }

        if (empty($errorEpisodeCodeList)) {
            return true;
        }

        $queryModuleCodeList = [
            'design',
            'asset',
            'level',
            'shot',
        ];
        $queryModuleIdList = [];
        foreach ($queryModuleCodeList as $moduleCode) {
            $queryModuleIdList[] = module_by_code($moduleCode)['id'];
        }
        $defaultClosedStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();
        $defaultDoneStatus = StatusService::getInstance()->getTaskDoneStatus();
        // 检查未完成环节任务
        $episodeNotDoneStepCategoryList = model(TaskModel::class)
            ->alias('t')
            ->join('entity e on e.id = t.entity_id')
            ->where([
                't.step_type' => TaskModel::STEP_TYPE_STEP_CATEGORY,
                'e.project_id' => $projectId,
                'e.module_id' => ['IN', implode(',', $queryModuleIdList)],
                'e.initial_episode_code' => ['IN', implode(',', $errorEpisodeCodeList)],
            ])
            ->where(sprintf("t.json->>'$.task_status' not in (%s)", implode(',', [$defaultClosedStatus['id'], $defaultDoneStatus['id']])))
            ->group('e.initial_episode_code')
            ->field('e.initial_episode_code,group_concat(distinct t.step_category_id) as step_category_ids')
            ->select();
        if (empty($episodeNotDoneStepCategoryList)) {
            return true;
        }

        $this->buildErrorMessage($episodeNotDoneStepCategoryList, $errorEpisodeTimeList);
        return false;
    }


    /**
     * 获取错误信息
     * @return array
     */
    public function getErrorMessage()
    {
        return $this->errorMessage;
    }


    /**
     * 查询剧集封板延期时长
     * @return string
     */
    private function getEpisodeLimitHourCheckConfig()
    {
        return OptionsService::getInstance()->getOptionsConfigItemData('project_warning_check', 'episode_seal_done_check', null);
    }

    /**
     * 生成错误信息
     * @param $episodeNotDoneStepCategoryList
     * @param $errorEpisodeTimeList
     * @return void
     */
    private function buildErrorMessage($episodeNotDoneStepCategoryList, $errorEpisodeTimeList)
    {
        $episodeNotDoneStepCategoryList = array_column($episodeNotDoneStepCategoryList, 'step_category_ids', 'initial_episode_code');

        $stepCategoryIdList = implode(',', $episodeNotDoneStepCategoryList);
        $stepCategoryIdList = array_unique(explode(',', $stepCategoryIdList));
        $stepCategoryList = model(StepCategoryModel::class)
            ->where(['id' => ['IN', $stepCategoryIdList]])
            ->field('id,name')
            ->select();
        $stepCategoryList = array_column($stepCategoryList, 'name', 'id');

        $this->errorMessage = [];
        foreach ($episodeNotDoneStepCategoryList as $episodeCode => $stepCategoryIds) {
            $stepCategoryIdList = explode(',', $stepCategoryIds);
            $stepCategoryNames = [];
            foreach ($stepCategoryIdList as $stepCategoryId) {
                if (!isset($stepCategoryList[$stepCategoryId])) {
                    continue;
                }
                $stepCategoryNames[] = $stepCategoryList[$stepCategoryId];
            }
            $surplusTime = $errorEpisodeTimeList[$episodeCode] ?? 0;
            $surplusDays = floor(abs($surplusTime) / 3600 / 24);
            if ($surplusTime > 0) {
                $errorText = '距离' . $episodeCode . '计划封板时间不到' . $surplusDays . '天, ' . implode(',', $stepCategoryNames) . '环节还未完成';
            } else {
                $errorText = '将要超出' . $episodeCode . '计划封板时间' . $surplusDays . '天, ' . implode(',', $stepCategoryNames) . '环节还未完成';
            }
            $this->errorMessage[] = $errorText;
        }
    }

    /**
     * @inheritDoc
     */
    public function getCheckCode()
    {
        return self::CHECK_CODE;
    }
}
